//
// Created by gj on 16-2-11.
//

#ifndef ALLUTILS_BASE64_H
#define ALLUTILS_BASE64_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

/**
 * Translation Table as described in RFC1113
 */
static const uint8_t cb64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const uint8_t cu64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
/**
 * Translation Table to decode (created by author)
 */
static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";

/**
 * returnable errors
 *
 * Error codes returned to the operating system.
 *
 */
#define BASE64_SUCCESS                0
#define BASE64_SYNTAX_ERROR            1
#define BASE64_FILE_ERROR            2
#define BASE64_FILE_IO_ERROR        3
#define BASE64_ERROR_OUT_CLOSE        4
#define BASE64_LINE_SIZE_TO_MIN        5
#define BASE64_SYNTAX_TOOMANYARGS    6

/**
 * Default values for encoder/decoder flags.
 */
#define BASE64_FLAG_DEFAULT        0
/**
 * Encoder flag bit to omit the padding '=' characters at the end
 * of the output (if any).
 */
#define BASE64_FLAG_NO_PADDING        0x01
/**
 * Encoder flag bit to omit all line terminators (i.e., the output
 * will be on one long line).
 */
#define BASE64_FLAG_NO_WRAP        0X02
/**
 * Encoder flag bit to indicate lines should be terminated with a
 * CRLF pair instead of just an LF.  Has no effect if {@code
 * NO_WRAP} is specified as well.
 */
#define BASE64_FLAG_CRLF            0x04
/**
 * Encoder/decoder flag bit to indicate using the "URL and
 * filename safe" variant of Base64 (see RFC 3548 section 4) where
 * {@code -} and {@code _} are used in place of {@code +} and
 * {@code /}.
 */
#define BASE64_FLAG_URL_SAFE        0x08

/**
 * b64_message
 *
 * Gather text messages in one place.
 *
 */
#define BASE64_MAX_MESSAGES 7
static const char *base64_msgs[BASE64_MAX_MESSAGES] = {"b64:000:Invalid Message Code.", //
                                                       "b64:001:Syntax Error -- check help (-h) for usage.", //
                                                       "b64:002:File Error Opening/Creating Files.", //
                                                       "b64:003:File I/O Error -- Note: output file not removed.", //
                                                       "b64:004:Error on output file close.", //
                                                       "b64:005:linesize set to minimum.", //
                                                       "b64:006:Syntax: Too many arguments."};

#define base64_message(ec) ((ec > 0 && ec < BASE64_MAX_MESSAGES ) ? base64_msgs[ ec ] : base64_msgs[ 0 ])
// 默认的换行界限
#define BASE64_DEF_LINE_SIZE   76
#define BASE64_MIN_LINE_SIZE    4

#define THIS_OPT(ac, av) ((char)(ac > 1 ? av[1][0] == '-' ? av[1][1] : 0 : 0))

/**
 * 计算encode后的长度
 */
int32_t base64_encode_buffsize(int32_t inSize, int32_t flags);
/**
 * 计算decode后的最大长度 （不可以作为实际长度使用）
 * decode以后的实际长度会比计算后的长度小一点，这个长度仅供开缓冲区使用
 * 并且计算长度是按照没有CRLF的情况计算 （指整个串没有CRLF）
 */
int32_t base64_decode_buffsize(int32_t outSize);

/**
 * 编码
 * 从文件中读入输出到文件中
 * 保证输入文件流是可读的，输出文件流是可写的
 */
int32_t base64_encode_forfile(FILE *infile, FILE *outfile, int32_t flags);
/**
 * 解码
 * 从文件中读入输出到文件中
 * 保证输入文件流是可读的，输出文件流是可写的
 */
int32_t base64_decode_forfile(FILE *infile, FILE *outfile);

/**
 * 编码
 * 从内存到内存
 * @input 	输入流
 * @inSize 	输入流大小
 * @output 	输出流; 保证输出流是可用的足够大的; 其大小可以由@base64_encodeBuffSize(inSize，flags)指定
 * @outSize	输出流大小; 指定上面输出缓冲区的大小; 在解码完成后会修改这个值为实际长度
 * @flags 	实现不同的编码方式; 参见以BASE64_FLAG开头的变量
 */
int32_t base64_encode(const uint8_t *input, int32_t inSize, uint8_t *output, int32_t &outSize, int32_t flags);
/**
 * 解码
 * 从内存到内存
 * @input 	输入流
 * @inSize 	输入流大小
 * @output 	输出流; 保证输出流是可用的足够大的; 其大小可以由@base64_decodeBuffSize(inSize)指定
 * @outSize	输出流大小; 指定上面输出缓冲区的大小; 在解码完成后会修改这个值为实际长度
 */
int32_t base64_decode(const uint8_t *input, int32_t inSize, uint8_t *output, int32_t &outSize);

#ifdef __cplusplus
}
#endif

#endif //ALLUTILS_BASE64_H
